/*
 * ProtocolByIdFinder.java
 *
 * Created on March 27, 2007, 11:09 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.atomojo.app;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import org.atomojo.app.db.DB;
import org.atomojo.app.db.Entry;
import org.atomojo.app.db.EntryMedia;
import org.atomojo.app.db.Feed;
import org.atomojo.app.db.Term;
import org.atomojo.app.db.TermInstance;
import org.infoset.xml.DocumentLoader;
import org.infoset.xml.sax.SAXDocumentLoader;
import org.restlet.Application;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.MediaType;
import org.restlet.data.Reference;
import org.restlet.data.Status;
import org.restlet.resource.Finder;
import org.restlet.resource.ServerResource;

/**
 *
 * @author alex
 */
public class QueryFinder extends Finder {

   public static class QuerySpec {
      Storage.Query query;
      boolean collection;
      MediaType mediaType;
      QuerySpec(Storage.Query query,boolean collection,MediaType mediaType)
      {
         this.query = query;
         this.collection = collection;
         this.mediaType = mediaType;
      }
      public Storage.Query getQuery() {
         return query;
      }
      
      public boolean useCollection() {
         return collection;
      }
      
      public MediaType getMediaType() {
         return mediaType;
      }
   }
   
   DB atomDB;
   Application theApp;
   Reference resourceBase;
   DocumentLoader loader = new SAXDocumentLoader();
   Storage storage;
   App app;
   Map<String,QuerySpec> queries;
   long expiration = 60*1000*5;
   long loadedAt;

   /** Creates a new instance of ProtocolByIdFinder */
   public QueryFinder(Context context,Application theApp,Reference resourceBase,DB atomDB,Storage storage) {
      super(context);
      this.theApp = theApp;
      this.atomDB = atomDB;
      this.resourceBase = resourceBase;
      this.storage = storage;
      this.app = new App(context.getLogger(),atomDB,storage,theApp.getMetadataService());
      this.queries = new TreeMap<String,QuerySpec>();
      this.loadedAt = -1;
      setTargetClass(ServerResource.class);
   }
   
   protected void loadQueries() 
      throws SQLException,IOException
   {
      getContext().getLogger().info("Reloading queries:");
      queries.clear();
      Term queryTerm = atomDB.findTerm(Categorization.QUERY_TERM);
      if (queryTerm==null) {
         queryTerm = atomDB.createTerm(Categorization.QUERY_TERM);
      }
      Term nameTerm = atomDB.findTerm(Categorization.QUERY_NAME_TERM);
      if (nameTerm==null) {
         nameTerm = atomDB.createTerm(Categorization.QUERY_NAME_TERM);
      }
      Term collectionTerm = atomDB.findTerm(Categorization.QUERY_USE_COLLECTION_TERM);
      if (collectionTerm==null) {
         collectionTerm = atomDB.createTerm(Categorization.QUERY_USE_COLLECTION_TERM);
      }
      Term mediaTypeTerm = atomDB.findTerm(Categorization.QUERY_MEDIA_TYPE_TERM);
      if (mediaTypeTerm==null) {
         mediaTypeTerm = atomDB.createTerm(Categorization.QUERY_MEDIA_TYPE_TERM);
      }
      loadedAt = System.currentTimeMillis();
      Iterator<TermInstance<Entry>> entries = atomDB.getEntriesByTerm(queryTerm);
      while (entries.hasNext()) {
         Entry entry = entries.next().getTarget();
         getContext().getLogger().info("Query entry: "+entry.getUUID());
         Feed feed = entry.getFeed();
         Iterator<EntryMedia> media = entry.getResources();
         if (media.hasNext()) {
            Storage.Query query = storage.getQuery(feed.getPath(),feed.getUUID(),media.next().getName());
            if (query!=null) {
               getContext().getLogger().info("Query found...");
               TermInstance<Entry> nameT = entry.getTerm(nameTerm);
               TermInstance<Entry> useCollectionT = entry.getTerm(collectionTerm);
               TermInstance<Entry> mediaTypeT = entry.getTerm(mediaTypeTerm);
               QuerySpec spec = new QuerySpec(query,useCollectionT!=null,mediaTypeT==null ? MediaType.APPLICATION_ATOM_XML : MediaType.valueOf(mediaTypeT.getValue().toString()));
               if (nameT!=null) {
                  String name = nameT.getValue()!=null ? nameT.getValue().toString() : null;
                  if (name!=null) {
                     getContext().getLogger().info("Query found, provided by name at "+name);
                     queries.put(name,spec);
                  }
               }
               queries.put(entry.getUUID().toString(),spec);
            }
         }
      }
   }
   
   public ServerResource fine(Request request, Response response) {
      
      String uriPath = request.getResourceRef().getRemainingPart();
      int q = uriPath.indexOf("?");
      if (q>0) {
         uriPath = uriPath.substring(0,q);
      }
      if (uriPath==null || uriPath.length()==0) {
         try {
            loadQueries();
            response.setStatus(Status.SUCCESS_NO_CONTENT);
            return null;
         } catch (Exception ex) {
            getContext().getLogger().log(Level.SEVERE,"Cannot load queries.",ex);
            response.setStatus(Status.SERVER_ERROR_INTERNAL,"Cannot load queries.");
            return null;
         }
      } else {
         String [] segments = uriPath.split("/");
         String queryName = segments[0];
         try {
            Feed feed = atomDB.findFeedByPath(segments,1,segments.length-1);
            if (feed==null) {
               return null;
            }
            if (loadedAt<0 || System.currentTimeMillis()>(loadedAt+expiration)) {
               loadQueries();
            }
            QuerySpec spec = queries.get(queryName);
            if (spec==null) {
               return null;
            }
            return new QueryResource(resourceBase,app,storage,feed,spec);
         } catch (Exception ex) {
            getContext().getLogger().log(Level.SEVERE,"Cannot load queries.",ex);
            response.setStatus(Status.SERVER_ERROR_INTERNAL,"Cannot load queries.");
            return null;
         }
      }
   }
   
   
}
